package com.cx.file.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.cx.file.config.FileManageConfigProperties;
import com.cx.file.exception.FileManageException;
import com.cx.file.service.FileManageService;
import org.springframework.http.MediaType;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.URLEncoder;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.List;

/**
 * @author cx
 * @description: 文件管理服务
 * @date 2023/6/2 17:14
 */
public class FileManageServiceImpl implements FileManageService {
    private static final String SUFFIX_SEPARATOR = ".";

    private final FileManageConfigProperties fileManageConfigProperties;

    public FileManageServiceImpl(FileManageConfigProperties fileManageConfigProperties) {
        this.fileManageConfigProperties = fileManageConfigProperties;
    }

    @Override
    public String uploadFile(String dir, MultipartFile file) throws IOException {
        String relativePath = "";
        if (StrUtil.isNotBlank(dir)) {
            relativePath = dir + File.separator;
        }
        String savePath = fileManageConfigProperties.getFileSavePath(relativePath);
        Path rootLocation = Paths.get(savePath);
        if (!Files.exists(rootLocation)) {
            Files.createDirectories(rootLocation);
        }
        String originalFilename = file.getOriginalFilename();
        if (StrUtil.isBlank(originalFilename)) {
            originalFilename = IdUtil.simpleUUID();
        }
        List<String> fileNameSplit = StrUtil.split(originalFilename, SUFFIX_SEPARATOR);
        String name = "";
        String type = "";
        int splitSize = CollUtil.size(fileNameSplit);
        if (splitSize<=0){
            throw new FileManageException(String.format("文件名称不合法(%s)", originalFilename));
        } else if (splitSize == 1) {
            name = fileNameSplit.get(0);
        } else{
            name = StrUtil.join(SUFFIX_SEPARATOR,fileNameSplit.subList(0,splitSize-1));
            type = fileNameSplit.get(splitSize-1);
        }
        List<String> includeTypes = fileManageConfigProperties.getIncludeTypes();
        if (CollUtil.isNotEmpty(includeTypes) && !includeTypes.contains(type)) {
            throw new FileManageException(String.format("文件类型不在许可类型之内(%s)", type));
        }
        List<String> excludeTypes = fileManageConfigProperties.getExcludeTypes();
        if (CollUtil.isNotEmpty(excludeTypes) && excludeTypes.contains(type)) {
            throw new FileManageException(String.format("文件类型已被禁止(%s)", type));
        }
        Path filePath = rootLocation.resolve(originalFilename);
        if (Files.exists(filePath)) {
            originalFilename = name + "-" + IdUtil.simpleUUID() + SUFFIX_SEPARATOR + type;
            filePath = rootLocation.resolve(originalFilename);
        }
        Files.write(filePath, file.getBytes());
        return relativePath + originalFilename;
    }

    @Override
    public void downloadFile(String filePath, HttpServletResponse response) {
        if (StrUtil.isBlank(filePath)) {
            throw new FileManageException("文件路径不能为空");
        }
        File file = getFileByPath(filePath);
        try (InputStream inputStream = Files.newInputStream(file.toPath());
             ServletOutputStream outputStream = response.getOutputStream()) {
            // 设置响应头
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
            response.setHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(file.getName(), "UTF-8") + "\"");
            // 将文件写入请求
            byte[] buffer = new byte[1024];
            int length;
            while ((length = inputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, length);
            }
            outputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
            throw new FileManageException("下载文件失败");
        }
    }

    @Override
    public void downloadFileZip(List<String> filePaths, HttpServletResponse response) throws IOException {
        if (CollUtil.isEmpty(filePaths)) {
            throw new FileManageException("文件路径不能为空");
        }
        String[] paths = new String[filePaths.size()];
        InputStream[] ins = new InputStream[filePaths.size()];
        for (int i = 0; i < filePaths.size(); i++) {
            String filePath = filePaths.get(i);
            File file = getFileByPath(filePath);
            paths[i] = file.getName();
            ins[i] = Files.newInputStream(file.toPath());
        }
        // 设置响应头
        response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);
        response.setHeader("Content-Disposition", "attachment; filename=\"" + DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN) + ".zip\"");
        ZipUtil.zip(response.getOutputStream(), paths, ins);
    }

    @Override
    public boolean deleteFile(String filePath) {
        if (StrUtil.isBlank(filePath)) {
            throw new FileManageException("文件路径不能为空");
        }
        String savePath = fileManageConfigProperties.getFileSavePath(filePath);
        File file = new File(savePath);
        if (file.isFile() && file.exists()) {
            return file.delete();
        } else {
            throw new FileManageException("目录不允许删除");
        }
    }

    private File getFileByPath(String path) {
        String savePath = fileManageConfigProperties.getFileSavePath(path);
        File file = new File(savePath);
        if (file.isDirectory()) {
            throw new FileManageException(String.format("必须为文件路径(%s)", path));
        }
        if (!file.exists()) {
            throw new FileManageException(String.format("文件不存在(%s)", path));
        }
        return file;
    }
}
